using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Magiq.Query;
using Magiq.Support;
using MirrorMirror;

namespace Magiq.Providers.Entities.Query
{
    public class MagiqToEntitiesCollectionQueryable<T,TChild> : IQueryable<TChild>, IMagiqToEntitiesCollectionQueryable where T:class
    {
        private readonly QueryInfo<T, TChild> queryInfo;
        private readonly IQueryable<TChild> baseQueryable;

        public MagiqToEntitiesCollectionQueryable(QueryInfo<T,TChild> queryInfo, ObjectContext objectContext)
        {
            this.queryInfo = queryInfo;
            Member = queryInfo.CollectionExpression.ToPropertyInfo();
            ParentKeyMembers = queryInfo.Item.Get<EntityKeyMember[]>("_entityWrapper.EntityKey.EntityKeyValues");
            var objectSet = objectContext.CreateObjectSet<T>();

            var parameter = Expression.Parameter(typeof(T), "x");

            Expression getByByBody = null;

            foreach (var keyMember in ParentKeyMembers)
            {
                var parentKeyName = keyMember.Key;
                Expression parentKeyValueExpression = Expression.Constant(keyMember.Value);

                var e = Expression.Equal(Expression.Property(parameter, parentKeyName), parentKeyValueExpression);
                getByByBody = getByByBody == null ? e : Expression.AndAlso(getByByBody, e);
            }

            var lambdaGetById = Expression.Lambda<Func<T, bool>>(getByByBody, parameter);

            baseQueryable = objectSet.Where(lambdaGetById).SelectMany(queryInfo.CollectionExpression);
        }

        public EntityKeyMember[] ParentKeyMembers { get; private set; }

        public Type ParentType { get { return typeof (T); } }

        public object Item
        {
            get { return queryInfo.Item; }
        }

        #region IQueryable<TChild> Members

        public IEnumerator<TChild> GetEnumerator()
        {
            return baseQueryable.GetEnumerator();
        }

        public Expression Expression
        {
            get { return baseQueryable.Expression; }
        }

        public Type ElementType
        {
            get { return baseQueryable.ElementType; }
        }

        public IQueryProvider Provider
        {
            get { return baseQueryable.Provider; }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        public IQueryable BaseQueryable
        {
            get { return baseQueryable; }
        }

        public PropertyInfo Member { get; private set; }
    }
}